import React, { useState, useEffect, useRef } from 'react';
import { Dialog, Button, Box } from '@mui/material';
import {
  NetPyNEHome,
  NetPyNEAddNew,
  NetPyNEThumbnail,
  GridLayout,
  Filter,
} from '../../general';
import ExpansionPanel from '../../general/ExpansionPanel';
import RulePath from '../../general/RulePath';
import Utils from '../../../utils/Utils';
import NetPyNEPopulation from './NetPyNEPopulation';

interface NetPyNEPopulationsProps {
  value?: Record<string, any>;
  model?: string;
  updateCards?: () => void;
  commands?: any[];
  [key: string]: any;
}

interface NetPyNEPopulationsState {
  drawerOpen: boolean;
  selectedPopulation?: string;
  populationDeleted?: string;
  errorMessage?: string;
  errorDetails?: string;
  filterPopValue: string | null;
}

const NetPyNEPopulations: React.FC<NetPyNEPopulationsProps> = ({
  value: propValue,
  model: propModel,
  updateCards,
  ...otherProps
}) => {
  const [state, setState] = useState<NetPyNEPopulationsState>({
    drawerOpen: false,
    selectedPopulation: undefined,
    populationDeleted: undefined,
    errorMessage: undefined,
    errorDetails: undefined,
    filterPopValue: null,
  });

  const prevValueRef = useRef<Record<string, any> | undefined>(propValue);

  useEffect(() => {
    // Handle rename detection
    const newPopulationName = hasSelectedPopulationBeenRenamed(
      prevValueRef.current,
      propValue
    );
    if (newPopulationName !== undefined) {
      setState((prev) => ({
        ...prev,
        selectedPopulation: newPopulationName,
        populationDeleted: undefined,
      }));
    } else if (
      prevValueRef.current !== undefined &&
      propValue !== undefined &&
      Object.keys(prevValueRef.current).length !== Object.keys(propValue).length
    ) {
      // Check for new items and validate names
      const currentModel = { ...propValue };
      let hasChanges = false;

      for (const m in currentModel) {
        if (prevValueRef.current && !(m in prevValueRef.current)) {
          let newValue = Utils.nameValidation(currentModel[m]?.name || m);
          if (newValue !== m) {
            newValue = Utils.getAvailableKey(currentModel, newValue);
            currentModel[newValue] = currentModel[m];
            if (currentModel[newValue]) {
              currentModel[newValue].name = newValue;
            }
            delete currentModel[m];
            hasChanges = true;

            Utils.renameKey('netParams.popParams', m, newValue, (response: any, newVal: string) => {
              if (updateCards) {
                updateCards();
              }
            });

            setState((prev) => ({
              ...prev,
              errorMessage: 'Error',
              errorDetails: `Leading digits or whitespaces are not allowed in Population names.\n${m} has been renamed ${newValue}`,
            }));
          }
        }
      }

      if (hasChanges && propValue !== currentModel) {
        // Update would need to be handled by parent component
      }
    }

    prevValueRef.current = propValue;
  }, [propValue, updateCards]);

  const hasSelectedPopulationBeenRenamed = (
    prevValue: Record<string, any> | undefined,
    currentValue: Record<string, any> | undefined
  ): string | undefined => {
    if (prevValue === undefined || currentValue === undefined) {
      return undefined;
    }

    const oldKeys = Object.keys(prevValue);
    const newKeys = Object.keys(currentValue);

    if (oldKeys.length === newKeys.length) {
      for (let i = 0; i < oldKeys.length; i++) {
        if (oldKeys[i] !== newKeys[i]) {
          if (state.selectedPopulation !== undefined && oldKeys[i] === state.selectedPopulation) {
            return newKeys[i];
          }
        }
      }
    }
    return undefined;
  };

  const handleNewPopulation = (): void => {
    const defaultPopulationValues = {
      Population: {
        cellModel: '',
        cellType: '',
      },
    };

    const key = Object.keys(defaultPopulationValues)[0];
    const value = defaultPopulationValues[key];
    const currentModel = propValue ? { ...propValue } : {};

    // Get New Available ID
    const populationId = Utils.getAvailableKey(currentModel, key);
    const newPopulation = { name: populationId, ...value };

    // Create Population Client side
    Utils.execPythonMessage(
      `netpyne_geppetto.netParams.popParams["${populationId}"] = ${JSON.stringify(value)}`
    );

    // Update would need to be handled by parent component state
    currentModel[populationId] = newPopulation;

    setState((prev) => ({
      ...prev,
      selectedPopulation: populationId,
    }));

    if (updateCards) {
      updateCards();
    }
  };

  const selectPopulation = (populationName: string): void => {
    setState((prev) => ({
      ...prev,
      selectedPopulation: populationName,
    }));
  };

  const handleRenameChildren = (childName: string): boolean => {
    childName = childName.replace(/\s*$/, '');
    if (!propValue) return false;
    const childrenList = Object.keys(propValue);
    for (let i = 0; i < childrenList.length; i++) {
      if (childName === childrenList[i]) {
        return false;
      }
    }
    return true;
  };

  const getPath = (): string | undefined => {
    if (propValue && state.selectedPopulation && propValue[state.selectedPopulation]) {
      return `netParams.popParams["${state.selectedPopulation}"]`;
    }
    return undefined;
  };

  // Render logic
  let dialogPop: React.ReactNode = null;
  if (state.errorMessage !== undefined) {
    dialogPop = (
      <Dialog open style={{ whiteSpace: 'pre-wrap' }}>
        <Box component="div" sx={{ p: 2 }}>
          <Box component="h2">{state.errorMessage}</Box>
          <Box component="pre" sx={{ overflow: 'auto' }}>
            {state.errorDetails}
          </Box>
        </Box>
        <Box sx={{ p: 2, display: 'flex', justifyContent: 'flex-end' }}>
          <Button
            variant="contained"
            color="primary"
            onClick={() =>
              setState((prev) => ({
                ...prev,
                errorMessage: undefined,
                errorDetails: undefined,
              }))
            }
          >
            BACK
          </Button>
        </Box>
      </Dialog>
    );
  }

  let populations: React.ReactNode[] = [];
  let selectedPopulation: React.ReactNode = null;

  if (propValue !== undefined && typeof propValue === 'object' && Object.keys(propValue).length > 0) {
    const model = { ...propValue };
    for (const m in model) {
      if (model[m]) {
        model[m].name = m;
      }
    }

    const filterName = state.filterPopValue === null ? '' : state.filterPopValue;
    populations = Object.keys(model)
      .filter((popName) => popName.toLowerCase().includes(filterName.toLowerCase()))
      .map((popName) => (
        <NetPyNEThumbnail
          key={popName}
          name={popName}
          selected={popName === state.selectedPopulation}
          paramPath="popParams"
          handleClick={(name: string) => selectPopulation(name)}
        />
      ));

    if (
      state.selectedPopulation !== undefined &&
      Object.keys(model).indexOf(state.selectedPopulation) > -1
    ) {
      selectedPopulation = (
        <NetPyNEPopulation
          name={state.selectedPopulation}
          model={model[state.selectedPopulation]}
          renameHandler={handleRenameChildren}
          updateCards={updateCards}
        />
      );
    }
  }

  return (
    <GridLayout>
      <div>
        <ExpansionPanel>
          <div className="breadcrumb">
            <div>
              <NetPyNEHome
                selection={state.selectedPopulation}
                handleClick={() =>
                  setState((prev) => ({ ...prev, selectedPopulation: undefined }))
                }
              />
              <div style={{ opacity: 0 }}>H</div>
            </div>

            <div>
              <NetPyNEAddNew
                id="newPopulationButton"
                title="Create new population"
                handleClick={handleNewPopulation}
              />
              <div
                style={{
                  textAlign: 'center',
                  fontFamily: 'Source Sans Pro',
                  maxWidth: 40,
                  overflow: 'visible',
                  display: 'flex',
                  justifyContent: 'center',
                }}
              >
                Population
              </div>
            </div>
          </div>
          <Box p={1}>
            <RulePath text={getPath()} />
            <Box mb={1} />
            <Filter
              value={state.filterPopValue}
              label="Filter population by name..."
              handleFilterChange={(newValue) =>
                setState((prev) => ({ ...prev, filterPopValue: newValue }))
              }
              options={propValue === undefined ? [] : Object.keys(propValue)}
            />
          </Box>
        </ExpansionPanel>
      </div>
      <Box className="scrollbar scrollchild" mt={1}>
        {populations}
      </Box>
      {selectedPopulation}
      {dialogPop}
    </GridLayout>
  );
};

export default NetPyNEPopulations;

